/**
 * Sphere.cpp
 *
 * Author:
 *   Chris Herbison
 *
 * Description:
 *   Represents a sphere of given radius and color.
 */

#include "Sphere.h"
#include <cmath>

#include <iostream>
#include <fstream>

/*
 * The constructor.
 *
 * _center - The coordinates of the center of the sphere (in world space).
 * _radius - The radius of the sphere.
 * _color - The color of the sphere.
 */
Sphere::Sphere(IlluminationData _material, TextureData _texture, Point3 _center, double _radius):
               Object(_material, _texture), center(_center), radius(_radius){
}

/*
 * Checks if a given ray intersects with this object.
 *
 * r - The ray to check for intersection.
 * data - Stores all the needed information from the intersection.
 *
 * return (bool) - Whether the ray intersects with this sphere or not.
 */
bool Sphere::intersects(Ray r, IntersectData* data/*, ofstream* outf*/){
     Vector3 rTemp(r.direction);
     rTemp.normalize();
     
     double dx = rTemp.x;
     double dy = rTemp.y;
     double dz = rTemp.z;
     double x0 = r.origin.x;
     double y0 = r.origin.y;
     double z0 = r.origin.z;

     double B = 2*((dx*(x0-center.x))+(dy*(y0-center.y))+(dz*(z0-center.z)));
     double C = ((x0-center.x)*(x0-center.x))+((y0-center.y)*(y0-center.y))
                +((z0-center.z)*(z0-center.z))-(radius*radius);
     
     double insideRoot = (B*B)-(4*C);

     //If root term is negative, no intersection exists.
     if(insideRoot < 0){
          return false;
     }
     
     //Calculate the two intersections (+-).
     double tPlus = ((-B)+sqrt(insideRoot))/(2);
     double tMinus = ((-B)-sqrt(insideRoot))/(2);
     
     Point3 intersectPlus(x0+dx*tPlus, y0+dy*tPlus, z0+dz*tPlus);
     Point3 intersectMinus(x0+dx*tMinus, y0+dy*tMinus, z0+dz*tMinus);
     
     //If both are negative, no intersection exists (behind camera).
     if(tPlus < 0 && tMinus < 0){
          return false;
     }
     else{
          //Add all relevant data to the IntersectData object.
          if(!(tMinus < 0) && intersectPlus.z > intersectMinus.z){
               (*data).pos = intersectMinus;
               (*data).normal = Vector3((intersectMinus.x-center.x)/radius,
                                        (intersectMinus.y-center.y)/radius,
                                        (intersectMinus.z-center.z)/radius);
          }
          else{
               (*data).pos = intersectPlus;
               (*data).normal = Vector3((intersectPlus.x-center.x)/radius,
                                        (intersectPlus.y-center.y)/radius,
                                        (intersectPlus.z-center.z)/radius);
          }
          vector<MyColor> colors(texture.getColors((*data).pos));
          (*data).CA = colors.at(0);
          (*data).CD = colors.at(1);
          (*data).CS = colors.at(2);
          Vector3 incoming((*data).pos - r.origin);
          incoming.normalize();
          (*data).normal.normalize();
          Vector3 tempArg((2*(incoming*(*data).normal))*(*data).normal);
          Vector3 reflect(incoming-tempArg);
          reflect.normalize();
          (*data).reflective = reflect;
          (*data).view = incoming;

          //If sphere is transmissive, calculate transmission ray.
          if(material.kt > 0){
               double nit;
               Vector3 theNormal = (*data).normal;
               if(parity){
                    nit = material.n/1;
                    theNormal = (-1)*theNormal;
               }
               else{
                    nit = 1/material.n;
               }
               incoming.normalize();
               theNormal.normalize();
               double cosTheta = ((-1)*incoming)*theNormal;
               double sqrtTerm = 1+((nit*nit)*((cosTheta*cosTheta)-1));
               //Check for total internal reflection.
               if(sqrtTerm < 0){
                    (*data).transmissive = (*data).reflective;
               }
               else{
                    (*data).transmissive = (nit*incoming)+((nit*cosTheta)-(sqrt(sqrtTerm)))*theNormal;
               }
               parity = !parity;
          }

          return true;
     }
}
